package com.uff.hmstpa.model.mediator;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.joda.time.DateTime;

import com.uff.hmstpa.model.ScheduleSystemModel;
import com.uff.hmstpa.model.decorator.BasicAppointment;
import com.uff.hmstpa.model.decorator.CoveredSpecialty;
import com.uff.hmstpa.model.decorator.HomeRide;
import com.uff.hmstpa.model.decorator.IAppointment;
import com.uff.hmstpa.model.decorator.UncoveredSpecialty;
import com.uff.hmstpa.model.domain.Appointment;
import com.uff.hmstpa.model.domain.Doctor;
import com.uff.hmstpa.model.domain.Healthplan;
import com.uff.hmstpa.model.domain.Patient;
import com.uff.hmstpa.model.persistence.impl.HealthplanDAOImpl;
import com.uff.hmstpa.model.service.AppointmentService;
import com.uff.hmstpa.model.service.CoverageService;
import com.uff.hmstpa.model.service.DoctorService;
import com.uff.hmstpa.model.service.PatientService;
import com.uff.hmstpa.util.command.impl.UpdateAppointmentsCommand;
import com.uff.hmstpa.util.command.impl.UpdateDoctorsCommand;
import com.uff.hmstpa.util.command.impl.UpdateHealthplansCommand;
import com.uff.hmstpa.util.command.impl.UpdatePatientsCommand;
import com.uff.hmstpa.util.date.DateParse;
import com.uff.hmstpa.util.exception.BusinessRuleException;
import com.uff.hmstpa.util.observer.Subject;

public class ModelMediator {

	private ScheduleSystemModel model;
	private DoctorService doctorService;
	private PatientService patientService;
	private AppointmentService appointmentService;
	private CoverageService coverageService;

	public void registerModel(ScheduleSystemModel model) {
		this.model = model;
		this.model.setMediator(this);
	}

	public void registerService(DoctorService doctorService) {
		this.doctorService = doctorService;
		this.doctorService.setMediator(this);
	}

	public void registerService(PatientService patientService) {
		this.patientService = patientService;
		this.patientService.setMediator(this);
	}

	public void registerService(AppointmentService appointmentService) {
		this.appointmentService = appointmentService;
		this.appointmentService.setMediator(this);
	}

	public void registerService(CoverageService coverageService) {
		this.coverageService = coverageService;
		this.coverageService.setMediator(this);
	}

	public void loadModel() {
		loadDoctors();
		loadPatients();
		loadAppointments();
		loadHealthplans();		
	}

	private void loadDoctors() {
		List<Doctor> doctors = doctorService.retrieveDoctors();

		for (Doctor doctor : doctors) {
			model.addDoctor(doctor);
		}

		((Subject) model).notifyObservers(new UpdateDoctorsCommand());
	}

	private void loadPatients() {
		List<Patient> patients = patientService.retrieveAllPatients();

		for(Patient p : patients) {
			model.addPatient(p);
		}

		((Subject) model).notifyObservers(new UpdatePatientsCommand());		
	}

	private void loadAppointments() {
		List<Appointment> appointments = appointmentService.retrieveAppointmentList(getSelectedDoctor());

		for (Appointment appointment : appointments) {
			model.addAppointment(appointment);
		}	

		((Subject) model).notifyObservers(new UpdateAppointmentsCommand());
	}

	private void loadHealthplans() {
		List<Healthplan> healthplans = HealthplanDAOImpl.getInstance().load();
		for(Healthplan hp : healthplans) {
			model.addHealthplan(hp);
		}

		((Subject) model).notifyObservers(new UpdateHealthplansCommand());
	}

	public List<Doctor> retrieveDoctors() {
		return model.getDoctors();
	}

	public void setSelectedDoctor(String doctorName) {
		if (doctorName.equalsIgnoreCase("TODOS")) {
			model.setSelectedDoctor(null);
			model.setAppointments(appointmentService.retrieveAppointmentList(null));
		} else {
			List<Doctor> doctors = model.getDoctors();

			for (int i = 0; i < doctors.size(); i++) {
				if (doctors.get(i).getName().equalsIgnoreCase(doctorName)) {
					model.setSelectedDoctor(doctors.get(i));
					model.setAppointments(appointmentService.retrieveAppointmentList(doctors.get(i)));
				}
			}
		}

		((Subject) model).notifyObservers(new UpdateAppointmentsCommand());
	}

	public Doctor getSelectedDoctor() {
		return model.getSelectedDoctor();
	}

	public void createAppointment(String crm, String cpf, String time, boolean homeride) throws BusinessRuleException {

		try {

			Doctor doctor = doctorService.retrieveDoctor(crm);
			Patient patient = patientService.retrievePatient(cpf);
			DateTime dateTime = new DateTime(DateParse.parseStringDate(time).getTime());
			
			//Appointment básico - o custo é o valor base da consulta de determinada specialidade
			IAppointment app = new BasicAppointment(doctor.getSpecialty());

			/*
			 * Caso o plano do paciente dê cobertura à especialidade em questão, utiliza-se o 
			 * decorador para calcular o preço
			 */
			if(coverageService.isCoveredByPlan(doctor.getSpecialty(), patient.getHealthplan())) {
				app = new CoveredSpecialty(app);
			} else {
				app = new UncoveredSpecialty(app);
			}

			/*
			 * Caso o paciente tenha escolhido busca a domicílio, através do decorador calculamos
			 * o preço correto da consulta
			 */
			if(homeride) {
				app = new HomeRide(app);
			}

			app = new Appointment(app, doctor, patient);			

			appointmentService.createAppointment(doctor, patient, dateTime, app);

		} catch (BusinessRuleException ex) {
			Logger.getLogger(ModelMediator.class.getName()).log(Level.SEVERE, null, ex);
			throw ex;
		}

		model.setAppointments(appointmentService.retrieveAppointmentList(model.getSelectedDoctor()));

		((Subject) model).notifyObservers(new UpdateAppointmentsCommand());
	}

	public void editAppointment(String crm, String cpf, DateTime time) throws BusinessRuleException {
		//TODO edição de appointment no mediador
		throw new UnsupportedOperationException("Edição de consulta não implementada");
	}

	public void deleteAppointment(String crm, String cpf, DateTime time) throws BusinessRuleException {
		appointmentService.removeAppointment(crm, cpf, DateParse.parseDateToString(time));
		model.setAppointments(appointmentService.retrieveAppointmentList(model.getSelectedDoctor()));

		((Subject) model).notifyObservers(new UpdateAppointmentsCommand());
	}

	public void createPatient(String cpf, String name, int healthplan) throws BusinessRuleException {
		Patient p = patientService.createPatient(cpf, name, healthplan);
		model.addPatient(p);

		((Subject) model).notifyObservers(new UpdatePatientsCommand());		
	}

	public void editPatient(String cpf, String name, int healthplan) throws BusinessRuleException {
		//TODO edição de patient no mediador
		throw new UnsupportedOperationException("Edição de paciente não implementada");
	}

	public void deletePatient(String cpf) throws BusinessRuleException {
		patientService.deletePatient(cpf);
	}

}
